package service

import (
	"api_interface/dao"
	"api_interface/entity"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"io"
	"math/big"
	"net/mail"
	"strings"
	"time"

	"github.com/dgrijalva/jwt-go"
	"gopkg.in/gomail.v2"
)

// 创建JWT
func createJWT(id string) (string, error) {
	// 创建一个新的 JWT
	token := jwt.New(jwt.SigningMethodHS256)

	// 添加标准声明
	claims := token.Claims.(jwt.MapClaims)
	claims["exp"] = time.Now().Add(time.Hour * 5).Unix() // 过期时间：5小时

	// 添加自定义声明
	claims["id"] = id

	// 签名并获取字符串表示
	var user entity.AccountInfo
	dao.SqlSession.Where("id = ?", id).First(&user)
	decodedKey, _ := base64.StdEncoding.DecodeString(user.Pwdkey)
	tokenString, err := token.SignedString(decodedKey)
	if err != nil {
		return "", err
	}

	return tokenString, nil
}

// 解析JWT
func parseJWT(tokenString string, id string) error {
	// 解析 JWT
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		// 验证签名方法
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("不支持的签名方法: %v", token.Header["alg"])
		}
		// 从数据库中获取密钥
		var user entity.AccountInfo
		dao.SqlSession.Where("id = ?", id).First(&user)
		fmt.Println(user.Email)
		decodedKey, _ := base64.StdEncoding.DecodeString(user.Pwdkey)
		return decodedKey, nil
	})
	if err != nil {
		return err
	}

	// 验证 token 中的声明
	if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
		fmt.Println("账号id:", claims["id"])
		fmt.Println("过期时间:", claims["exp"])
		return nil
	} else {
		return fmt.Errorf("无效的 token")
	}

}

/*
登录
错误有两种：
 1. 用户不存在 -> msg = "user not exist", code = 1
 2. 密码错误 -> msg = "wrong pwd", code = 2
*/
func Login(id string, remember bool, password string) (code int, msg string, data string) {
	var user entity.AccountInfo

	// 进入邮箱中查找
	dao.SqlSession.Where("email = ?", id).First(&user)
	if user.Email == "" {
		return 1, "user not exist", ""
	}

	// 将密码从base64解码
	decodePwd, _ := base64.StdEncoding.DecodeString(user.Pwd)
	// 将密钥从base64解码
	decodeKey, _ := base64.StdEncoding.DecodeString(user.Pwdkey)
	// 将iv从base64解码
	iv, _ := base64.StdEncoding.DecodeString(user.Pwdiv)
	// 根据密钥创建一个AES加密算法的块
	block, _ := aes.NewCipher(decodeKey)
	// 解密
	stream := cipher.NewCFBDecrypter(block, iv)
	stream.XORKeyStream(decodePwd, decodePwd)
	fmt.Println(string(decodePwd))
	// 验证输入的密码是否与数据库的对应
	if password == string(decodePwd) {
		// 创建JWT
		tokenString, _ := createJWT(id)
		fmt.Println("tokenString: ", tokenString)
		return 0, "success", tokenString
	} else {
		return 2, "wrong pwd", ""
	}
}

/*
注册
邮箱和密码必填
*/
func Register(email string, password string, name string) (code int, msg string) {
	fmt.Println(email, password, name)
	// 根据邮箱后缀判断是用户还是游客
	parts := strings.Split(email, "@")
	fmt.Println(parts)
	// 生成随机的32位密钥
	key := make([]byte, 32)
	if _, err := io.ReadFull(rand.Reader, key); err != nil {
		panic(err)
	}
	// 创建加密算法块
	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}
	data := []byte(password)
	// 创建加密方式
	cipherdata := make([]byte, aes.BlockSize+len(data))
	iv := cipherdata[:aes.BlockSize]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		panic(err)
	}
	stream := cipher.NewCFBEncrypter(block, iv)
	stream.XORKeyStream(cipherdata[aes.BlockSize:], data)
	// 将加密后的数据转换为 Base64 编码
	base64Pwd := base64.StdEncoding.EncodeToString(cipherdata[aes.BlockSize:])
	fmt.Println("Base64 编码后的数据为： ", base64Pwd)
	// 将密钥转换为 Base64 编码
	base64key := base64.StdEncoding.EncodeToString(key)
	// 将iv转换成 Base64 编码
	base64iv := base64.StdEncoding.EncodeToString(iv)
	if parts[1] == "mail.sysu.edu.cn" || parts[1] == "mail2.sysu.edu.cn" || parts[1] == "mail3.sysu.edu.cn" {
		// 用户
		dao.SqlSession.Model(&entity.AccountInfo{}).Where("email = ?", email).Update("pwd", base64Pwd).Update("nam", name).Update("typ", 1).Update("pwdkey", base64key).Update("pwdiv", base64iv)
	} else {
		// 游客
		dao.SqlSession.Model(&entity.AccountInfo{}).Where("email = ?", email).Update("pwd", base64Pwd).Update("nam", name).Update("typ", 2).Update("pwdkey", base64key).Update("pwdiv", base64iv)
	}
	return 0, "success"
}

/*
查询个人信息
*/
func GetInfo(email string, jwt string) (account entity.AccountInfo, code int, msg string) {
	// 解析和验证 JWT
	err := parseJWT(jwt, email)
	if err != nil {
		fmt.Println("解析 JWT 失败:", err)
		return
	}
	fmt.Println("JWT 解析成功")
	var user entity.AccountInfo
	dao.SqlSession.Where("email = ?", email).First(&user)
	fmt.Println("user: ", user)
	return user, 0, "success"
}

// 修改个人信息

func Modify(user entity.AccountInfo, jwt string) (code int, msg string) {
	// 解析和验证 JWT
	err := parseJWT(jwt, user.Email)
	if err != nil {
		fmt.Println("解析 JWT 失败:", err)
		return
	}
	fmt.Println("JWT 解析成功")
	dao.SqlSession.Model(&entity.AccountInfo{}).Where("id = ?", user.Id).Updates(&user)
	return 0, "success"
}

// 查询关注列表

func GetConcernInfo(id int, typ int, pageNum int, pageSize int) (account []entity.AccountInfo, total int, code int, msg string) {
	var uid []entity.ConcernedInfo
	var users []entity.AccountInfo

	if typ == 1 {
		dao.SqlSession.Model(&entity.ConcernedInfo{}).Where("fid = ?", id).Find(&uid)
		for i := 0; i < len(uid); i++ {
			var user entity.AccountInfo
			id := uid[i].Sid
			err := dao.SqlSession.Table("account_info").Select("id, nam, avatar").Where("id = ?", id).Find(&user).Error
			if err != nil {
				fmt.Println(err)
			}
			users = append(users, user)
		}
	} else {
		dao.SqlSession.Model(&entity.ConcernedInfo{}).Where("sid = ?", id).Find(&uid)
		for i := 0; i < len(uid); i++ {
			dao.SqlSession.Table("account_info").Select("id, nam, avatar").Where("id = ?", uid[i].Fid).Find(&users)
		}
	}
	return users, len(users), 0, "success"
}

func SendCode(email string) {
	m := gomail.NewMessage()
	// 生成随机6位验证码
	charset := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
	result := make([]byte, 6)
	charsetLen := len(charset)
	for i := 0; i < 6; i++ {
		randomIndex, _ := rand.Int(rand.Reader, big.NewInt(int64(charsetLen)))
		result[i] = charset[randomIndex.Int64()]
	}
	fmt.Println(string(result))
	// 将该验证码存入数据库
	dao.SqlSession.Model(&entity.AccountInfo{}).Where("email = ?", email).Update("emailcode", string(result))

	// 发送验证码到该邮箱
	m.SetHeader("From", "1831534298@qq.com")
	m.SetHeader("To", email)
	m.SetHeader("Subject", "来自匿名提问箱的验证码")
	m.SetBody("text/html", string(result))
	d := gomail.NewDialer("smtp.qq.com", 587, "1831534298@qq.com", "rlmrhxdcrkdlchdb")
	if err := d.DialAndSend(m); err != nil {
		fmt.Println("send email failed")
	}
}

/*
验证邮箱
错误类型：
 1. 邮箱不存在 -> msg = "email not exist"，code = 1
 2. 邮箱已注册 -> msg = "email has been registered"，code = 2
*/
func VerifyEmail(email string) (code int, msg string) {
	// 验证邮箱的输入是否合法
	_, err := mail.ParseAddress(email)
	if err != nil {
		return 1, "email not exist"
	}
	// 需要重发验证码的情况
	// 先判断邮箱是否存在于数据库
	var user entity.AccountInfo
	dao.SqlSession.Where("email = ?", email).First(&user)
	if user.Email != "" {
		// 再判断邮箱对应的密码是否为空
		if user.Pwd == "" {
			// 发送验证码
			SendCode(email)
			return 0, "This email is valid"
		} else {
			return 2, "email has been registered"
		}
	}
	/*
		// 验证邮箱是否存在
		m := gomail.NewMessage()
		m.SetHeader("From", "1831534298@qq.com")
		m.SetHeader("To", email)
		m.SetHeader("Subject", "欢迎使用匿名提问箱")
		m.SetBody("text/html", "这是一封测试邮件，用于测试邮箱是否可用")
		d := gomail.NewDialer("smtp.qq.com", 587, "1831534298@qq.com", "rlmrhxdcrkdlchdb")
		if err := d.DialAndSend(m); err != nil {
			return 1, "email not exist"
		}
	*/
	// 邮箱存在且未被注册，发送验证码到该邮箱
	// 将该邮箱插入数据库
	user.Email = email
	dao.SqlSession.Create(&user)
	// 生成随机6位验证码
	SendCode(email)
	return 0, "This email is valid"
}

/*
验证邮箱和验证码
错误类型：
 1. 邮箱和验证码不匹配 -> msg = "email and code do not match"，code = 1
*/
func VerifyEmailAndCode(email string, emailcode string) (code int, msg string) {
	var user entity.AccountInfo
	dao.SqlSession.Where("email = ?", email).First(&user)
	if user.Emailcode == emailcode {
		fmt.Println("email = ", email, "emailcode = ", emailcode)
		return 0, "success"
	} else {
		return 1, "email and code do not match"
	}
}

/*
重设密码
*/
func ResetPwd(email string, password string) (code int, msg string) {
	// 生成随机的32位密钥
	key := make([]byte, 32)
	if _, err := io.ReadFull(rand.Reader, key); err != nil {
		panic(err)
	}
	// 创建加密算法块
	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	data := []byte(password)
	// 创建加密方式
	cipherdata := make([]byte, aes.BlockSize+len(data))
	iv := cipherdata[:aes.BlockSize]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		panic(err)
	}
	stream := cipher.NewCFBEncrypter(block, iv)
	stream.XORKeyStream(cipherdata[aes.BlockSize:], data)
	// 将加密后的数据转换为 Base64 编码
	base64Pwd := base64.StdEncoding.EncodeToString(cipherdata[aes.BlockSize:])
	fmt.Println("Base64 编码后的数据为： ", base64Pwd)
	// 将密钥转换为 Base64 编码
	base64key := base64.StdEncoding.EncodeToString(key)
	// 将iv转换成 Base64 编码
	base64iv := base64.StdEncoding.EncodeToString(iv)
	fmt.Println("email = ", email, "password = ", password)
	dao.SqlSession.Model(&entity.AccountInfo{}).Where("email = ?", email).Update("pwd", base64Pwd).Update("pwdkey", base64key).Update("pwdiv", base64iv)
	return 0, "success"
}

/*
查找用户
*/

func FindAccount(id string) (account []entity.AccountInfo, total int, code int, msg string) {
	var users []entity.AccountInfo
	var user2 []entity.AccountInfo
	// 在用户名中查找
	dao.SqlSession.Table("account_info").Select("id, nam, avatar").Where("nam LIKE ?", "%"+id+"%").Find(&users)
	// 在id中查找
	dao.SqlSession.Table("account_info").Select("id, nam, avatar").Where("id LIKE ?", "%"+id+"%").Find(&user2)
	//fmt.Println(users)
	users = append(users, user2...)
	return users, len(users), 0, "success"
}

/*
	更改邮箱
*/

func UpdateEmail(id int, email string, jwt string) (code int, msg string) {
	// 解析和验证 JWT
	err := parseJWT(jwt, email)
	if err != nil {
		fmt.Println("解析 JWT 失败:", err)
		return 1, "解析 JWT 失败, 未处于登录状态"
	}
	err = dao.SqlSession.Model(&entity.AccountInfo{}).Where("id = ?", id).Update("email", email).Error
	if err != nil {
		panic(err)
	}
	return 0, "success"
}
